#include "amfEncoder.h"
#include "umapi_libc.h"
#include "umlog.h"

#define TARGET "AMFENCODE"

static UM_VOID AMFEncoderCheckSize(struct Encoder * penc, UM_S32 len)
{
	//LOGI("enc:%p, index:%d,len:%d, %d",
	//	penc, penc->index, len, penc->len);
	if(penc->index + len > penc->len)
	{
		penc->len += 4096;
		UM_U8 *p = penc->local_buf;
		penc->local_buf = umapi_malloc(penc->len );
		umapi_memcpy(penc->local_buf, p, penc->index);
		umapi_free(p);
	}
}

static UM_VOID AMFEncoderWrite(struct Encoder * penc, UM_U8 c)
{
	AMFEncoderCheckSize(penc, 1);
	penc->local_buf[penc->index++] = c;
	//LOGI("enc:%p, ch:%x index:%d", penc, c, penc->index);
}
static UM_VOID AMFEncoderWriteStr(struct Encoder * penc, UM_U8 *str, UM_S32 len)
{
	AMFEncoderCheckSize(penc, len);
	umapi_memcpy(penc->local_buf + penc->index, str, len);
	penc->index += len;
	//LOGI("enc:%p, str:%s index:%d", penc, str, penc->index);
}
static UM_U8 AMFEncoderget(struct Encoder * penc, UM_S32 index)
{
	return penc->local_buf[index];
}
static UM_S32 AMFEncodergetStr(struct Encoder * penc, UM_U8 **str)
{
	*str = penc->local_buf;
	return penc->index;
}
static UM_S32 AMFEncodergetSize(struct Encoder * penc)
{
	return penc->index;
}
static UM_VOID AMFEncoderClear(struct Encoder * penc)
{
	penc->index = 0;
}

static UM_VOID amf_write_string(Encoder *enc,const UM_CHAR *s)
{
	enc->write(enc, (UM_CHAR)(AMF0_STRING));
	UM_U16 str_len = htons(umapi_strlen(s));
	enc->writeStr(enc,(UM_CHAR *) &str_len, 2);
	enc->writeStr(enc, s, umapi_strlen(s));
}

static UM_VOID amf_write_int(Encoder *enc, int i)
{
	UM_S32 ii = htonl(i);
	enc->writeStr(enc, (char *) &ii, 4);
}

static UM_VOID amf_write_double(Encoder *enc, UM_DOUBLE n)
{
	enc->write(enc, (UM_CHAR)(AMF0_NUMBER));
	UM_U64 encoded = 0;
	memcpy(&encoded, &n, 8);
	UM_U32 val = htonl(encoded >> 32);
	enc->writeStr(enc,(char *) &val, 4);
	val = htonl(encoded);
	enc->writeStr(enc,(char *) &val, 4);
}

static UM_VOID amf_write_bool(Encoder *enc, UM_BOOL b)
{
	enc->write(enc,(UM_CHAR)(AMF0_BOOLEAN));
	enc->write(enc,(UM_CHAR)(b));
}

static UM_VOID amf_write_key(Encoder *enc, const UM_CHAR *s)
{
	UM_U16 str_len = htons(umapi_strlen(s));
	enc->writeStr(enc,(char *) &str_len, 2);
	enc->writeStr(enc, s, umapi_strlen(s));
}

static UM_VOID amf_write_object(Encoder *enc, const amf_object_t *object)
{
	enc->write(enc, (UM_CHAR)(AMF0_OBJECT));
	struct umapi_list_head *pos, *n;
	umapi_list_for_each_safe(pos,n, &object->head){
		AMFObject *i = umapi_list_entry(pos, AMFObject, head);
		amf_write_key(enc, i->first);
		amf_write(enc, i->second);
	}
	amf_write_key(enc, "");
	enc->write(enc, (UM_CHAR)(AMF0_OBJECT_END));
}

static UM_VOID amf_write_ecma(Encoder *enc, const amf_object_t *object)
{
	enc->write(enc, (UM_CHAR)(AMF0_ECMA_ARRAY));
	UM_U32 zero = 0;
	enc->writeStr(enc, (char *) &zero, 4);
	struct umapi_list_head *pos, *n;
	umapi_list_for_each_safe(pos,n, &object->head){
		AMFObject *i = umapi_list_entry(pos, AMFObject, head);
		amf_write_key(enc, i->first);
		amf_write(enc, i->second);
	}
	amf_write_key(enc, "");
	enc->write(enc, (UM_CHAR)(AMF0_OBJECT_END));
}

static UM_VOID amf_write_null(Encoder *enc)
{
	enc->write(enc,(UM_CHAR)AMF0_NULL);
}

UM_VOID amf_write(Encoder *enc, const AMFValue *value)
{
	if(value)
	{
		switch (value->m_type) {
		case AMF_STRING:
			amf_write_string(enc, value->as_string(value));
			break;
		case AMF_NUMBER:
			amf_write_double(enc, value->as_number(value));
			break;
		case AMF_INTEGER:
			amf_write_int(enc, value->as_integer(value));
			break;
		case AMF_BOOLEAN:
			amf_write_bool(enc, value->as_bool(value));
			break;
		case AMF_OBJECT:
			amf_write_object(enc, value->as_object(value));
			break;
		case AMF_ECMA_ARRAY:
			amf_write_ecma(enc, value->as_object(value));
			break;
		case AMF_NULL:
			amf_write_null(enc);
			break;
		}
	}
	else
		amf_write_null(enc);
}

static UM_VOID destoryAMFEncode(Encoder *penc)
{
	if(penc->local_buf)
	{
		umapi_free(penc->local_buf);
		penc->local_buf = UM_NULL;
	}
	umapi_free(penc);
}

Encoder* createAMFEncoder()
{
	Encoder* penc = (Encoder*)umapi_malloc(sizeof(Encoder));
	penc->len = 4096;
	penc->local_buf = umapi_malloc(penc->len);
	penc->index = 0;
	penc->free = destoryAMFEncode;
	penc->write = AMFEncoderWrite;
	penc->writeStr = AMFEncoderWriteStr;
	penc->get = AMFEncoderget;
	penc->getStr = AMFEncodergetStr;
	penc->getSize = AMFEncodergetSize;
	penc->clear = AMFEncoderClear;
	return penc;
}


